#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>
using namespace std;
const static int g_maxcap = 5;
template <class T>
class BlockQueue
{
public:
    BlockQueue(const int& maxcap = g_maxcap)
        :_maxcap(maxcap)
    {
        pthread_mutex_init(&_mutex,nullptr);
        pthread_cond_init(&_pcond,nullptr);
        pthread_cond_init(&_ccond,nullptr);
    }
    void push(const T& in)  //输入性参数：const &
    {
        pthread_mutex_lock(&_mutex);
        //1.先判断,队列为满，那么就不能生产了，生产者要等待
        // if(is_full())    //这里如果不是唤醒一个线程，而是唤醒所有线程，但是队列里只有一个空位置，
                            //那么多个线程往队列里放数据就会造成数据覆盖问题
        while(is_full())
        {   
            //pthread_cond_wait 函数的第二个参数，必须是正在使用的互斥锁。
            // 该函数在线程调用时，会以原子的方式将锁释放，并将自己挂起，
            // 该函数在线程被唤醒返回时，会自动的重新获取你传入的互斥锁
            pthread_cond_wait(&_pcond,&_mutex);
        }
        //2.此时队列里一定还可以存放数据
        _qe.push(in);
        //3. 此时队列里一定有数据，唤醒消费者线程
        pthread_cond_signal(&_ccond);   //这个函数可以放在临界区内，也可放在临界区外（建议放在里面）

        pthread_mutex_unlock(&_mutex);
    }
    void pop(T* out)        //输出性参数：*   ，  输入输出型 ：&
    {
        pthread_mutex_lock(&_mutex);
        //1.判断，队列为空，那么就不能消费了，消费者要等待
        // if(is_empty())   //这里也一样，唤醒多个线程，但是队列里只有一个数据供消费，
                            //那么就会取到以前的旧数据
        while(is_empty())
        {
            pthread_cond_wait(&_ccond,&_mutex);
        }
        //2. 此时队列里一定不为空
        *out = _qe.front();
        _qe.pop();
        //3. 此时队列里一定有位置可以存放数据，唤醒生产者线程
        pthread_cond_signal(&_pcond);   //这个函数可以放在临界区内，也可放在临界区外（建议放在里面）

        pthread_mutex_unlock(&_mutex);
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_pcond);
        pthread_cond_destroy(&_ccond);
    }
private:
    bool is_empty()
    {
        return _qe.empty();
    }
    bool is_full()
    {
        return _qe.size() == _maxcap;
    }
private:
    std::queue<T> _qe;
    int _maxcap;    //队列中存储元素的上线
    pthread_mutex_t _mutex;
    pthread_cond_t _pcond;  //生产者对应的条件队列
    pthread_cond_t _ccond;  //消费者对应的条件队列


};